﻿namespace Sai.Core.Introspection
{
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Text;

	/// <summary>
	/// Holds information about class declarations.
	/// </summary>
	public class ClassDeclaration : ReferenceTypeDeclaration, ITypeDeclaration, IEquatable<ITypeDeclaration>, ISourceCodeProvider
	{
		private TypeName baseType;

        private StaticConstructorDeclaration staticConstructor = null;

        private FinalizerDeclaration finalizer = null;

		private InstanceConstructorDeclarationCollection instanceConstructors = new InstanceConstructorDeclarationCollection();

		private FieldDeclarationCollection fields = new FieldDeclarationCollection();

		public ClassDeclaration()
			: base()
		{
		}

		public ClassDeclaration(TypeName typeName)
			: base(typeName)
		{
		}

		public ClassDeclaration(TypeName typeName, ObsoleteMarker marker)
			: base(typeName, marker)
		{
		}

		public ReadOnlyCollection<InstanceConstructorDeclaration> InstanceConstructors
		{
			get
			{
				return new ReadOnlyCollection<InstanceConstructorDeclaration>(this.instanceConstructors);
			}
		}

        public bool HasStaticConstructor
        {
            get
            {
                return this.staticConstructor != null;
            }
        }

        public StaticConstructorDeclaration StaticConstructor
        {
            get
            {
                return this.staticConstructor;
            }
        }

        public bool HasFinalizer
        {
            get
            {
                return this.finalizer != null;
            }
        }

        public FinalizerDeclaration Finalizer
        {
            get
            {
                return this.finalizer;
            }
        }

		public ReadOnlyCollection<FieldDeclaration> Fields
		{
			get
			{
				return new ReadOnlyCollection<FieldDeclaration>(this.fields);
			}
		}

		public bool IsSubclass
		{
			get
			{
				return this.baseType != null;
			}
		}

		public TypeTaxonomy Taxonomy
		{
			get
			{
				return TypeTaxonomy.Class;
			}
		}
		
		public TypeName BaseType
		{
			get
			{
				return this.baseType;
			}
		}

		public bool IsAbstract { get; set; }

		public bool IsSealed { get; set; }

		public void DerivedFrom(TypeName superClass)
		{
			this.baseType = superClass;
		}

		public void Add(InstanceConstructorDeclaration constructor)
		{
			this.instanceConstructors.Add(constructor);
		}

        public void Add(StaticConstructorDeclaration constructor)
        {
            if (this.staticConstructor != null)
                throw new ArgumentException("Only one static constructor allowed per type");

            this.staticConstructor = constructor;
        }

        public void Add(FinalizerDeclaration finalizer)
        {
            if (this.finalizer != null)
                throw new ArgumentException("Only one finalizer allowed per type");

            this.finalizer = finalizer;
        }

		public void Add(FieldDeclaration fieldInstance)
		{
			this.fields.Add(fieldInstance);
		}

		public bool MatchesType(ITypeDeclaration other)
		{
			return this.Taxonomy == other.Taxonomy;
		}

        public SourceCode ToCode()
        {
            SourceCode code = new SourceCode();

            code.Add(new KeywordTag("public"));

            if (this.IsAbstract)
            {
                code.Add(new KeywordTag("abstract"));
            }

            if (this.IsSealed)
            {
                code.Add(new KeywordTag("sealed"));
            }

            code.Add(new KeywordTag("class"));
            code.Add(new TypeNameTag(this.TypeName.FullName.ToString()));

            if (this.IsSubclass || this.Interfaces.Count > 0)
            {
                code.Add(new PunctuationTag(":"));
            }

            if (this.IsSubclass)
            {
                code.Add(new TypeNameTag(this.BaseType.Name));
            }

            if (this.IsSubclass && this.Interfaces.Count > 0)
            {
                code.Add(new PunctuationTag(","));
            }

            if (this.Interfaces.Count > 0)
            {
                for (int i = 0; i < this.Interfaces.Count; ++i)
                {
                    TypeName interf = this.Interfaces[i];

                    code.Add(new TypeNameTag(interf.Name));

                    if (i < this.Interfaces.Count - 1)
                    {
                        code.Add(new PunctuationTag(","));
                    }
                }
            }

            return code;
        }
    }
}
